package com.orange.adminapi.security.service;

import cn.hutool.core.collection.CollUtil;
import com.orange.adminapi.service.admin.AdminService;
import com.orange.adminapi.service.permission.AdminRoleService;
import com.orange.adminapi.service.permission.RolePermissionService;
import com.orange.adminapi.service.permission.RoleService;
import com.orange.core.pojo.entity.admin.Admin;
import com.orange.core.pojo.entity.permission.AdminRole;
import com.orange.core.pojo.entity.permission.Permission;
import com.orange.core.pojo.entity.permission.Role;
import com.orange.core.pojo.entity.permission.RolePermission;
import com.orange.core.pojo.enums.EPermission;
import com.orange.core.pojo.enums.ERole;
import com.orange.core.pojo.enums.EStatus;
import com.orange.core.security.detail.AdminDetail;
import com.orange.core.util.TokenUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@RequiredArgsConstructor
@Service
public class AdminDetailService implements UserDetailsService {

    private final AdminService adminService;
    private final AdminRoleService adminRoleService;
    private final RoleService roleService;
    private final RolePermissionService rolePermissionService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Admin admin = adminService.lambdaQuery()
                .eq(Admin::getLoginName, username)
                .one();
        if (admin == null) {
            return null;
        }

        Set<Integer> roleIds = adminRoleService.lambdaQuery()
                .eq(AdminRole::getAdminId, admin.getId())
                .list().stream().map(AdminRole::getRoleId).collect(Collectors.toSet());
        // 用于持久化的用户角色代码集合
        List<String> roleCodes = new ArrayList<>();
        // 用于持久化的用户权限标识符集合
        List<String> permissionIdentifiers = new ArrayList<>();
        // 用户角色权限集合
        Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
        if (CollUtil.isNotEmpty(roleIds)) {
            List<Role> roles = roleService.validList(roleIds);
            // 用户角色集合
            Set<SimpleGrantedAuthority> roleAuthorities = roles.stream().map(Role::getCode).peek(roleCodes::add).map(SimpleGrantedAuthority::new).collect(Collectors.toSet());
            // 用户权限集合
            List<GrantedAuthority> permissionAuthorities = new ArrayList<>();
            if (roles.stream().map(Role::getCode).anyMatch(code -> code.equalsIgnoreCase(ERole.ADMIN.getCode()))) {
                // 超级管理员自动取得所有权限
                permissionAuthorities = Arrays.stream(EPermission.values()).map(EPermission::getPermission).map(Permission::getIdentifier)
                        .peek(permissionIdentifiers::add).map(SimpleGrantedAuthority::new).collect(Collectors.toList());
            } else {
                Set<Integer> roleIdList = roles.stream().map(Role::getId).collect(Collectors.toSet());
                if (CollUtil.isNotEmpty(roleIdList)) {
                    permissionAuthorities = rolePermissionService.lambdaQuery()
                            .in(RolePermission::getRoleId, roleIdList)
                            .list().stream().map(RolePermission::getPermissionId).map(EPermission::byId).filter(Objects::nonNull)
                            .map(Permission::getIdentifier).peek(permissionIdentifiers::add).map(SimpleGrantedAuthority::new).collect(Collectors.toList());
                }
            }
            grantedAuthorities.addAll(roleAuthorities);
            grantedAuthorities.addAll(permissionAuthorities);
        }

        return new AdminDetail(admin.getId(), username, admin.getPassword(), admin.getStatus() == EStatus.NORMAL.getValue(), true,
                true, true, grantedAuthorities)
                .setToken(TokenUtil.newToken())
                .setRoles(roleCodes)
                .setPermissions(permissionIdentifiers);
    }
}
